import cairo
import ctypes
from pyglet.gl import *
import pyglet
import windoh


class CairoStyle(windoh.Style):
    cairo_initial = {
        'Control': {
            'background_color': None,
            'border_style': None,
            'border_width': 1.0,
            'border_color': (0.0, 0.0, 0.0),
            'border_radius': None,
        },
        'Label': {
            'font_size': 12,
            'font_face': 'Sans',
            'font_color': (1.0, 1.0, 1.0),
        },
        'Button': {
            'border_style': "single",
            'border_width': 1.5,
            'border_color': (1.0, 1.0, 1.0),
            'border_radius': 5,
            'background_color': (0.5, 0.5, 0.5),
        },
        'Frame': { 
            'border_style': "single",
            'border_width': 1.5,
            'border_color': (1.0, 1.0, 1.0),
            'border_radius': 5,
            'background_color': (0.5, 0.5, 0.5),
            'title_font_size': 12,
            'title_font_face': 'Sans',
            'title_font_color': (1.0, 1.0, 1.0),
            'title_height': 20,
        },
        'Window': {
            'title_background_color': (0.4, 0.4, 0.4),
            'resize_handle_size': 10,
        },
    }

    def __init__(self, owner=None, initial=None):
        style = self.cairo_initial.copy()
        if initial:
            style.update(initial)
        windoh.Style.__init__(self, owner=owner, initial=style)


def roundedrec(context,x,y,w,h,r = 10):
    "Draw a rounded rectangle"
    #   A****BQ
    #  H      C
    #  *      *
    #  G      D
    #   F****E

    context.move_to(x+r,y)                      # Move to A
    context.line_to(x+w-r,y)                    # Straight line to B
    context.curve_to(x+w,y,x+w,y,x+w,y+r)       # Curve to C, Control points are both at Q
    context.line_to(x+w,y+h-r)                  # Move to D
    context.curve_to(x+w,y+h,x+w,y+h,x+w-r,y+h) # Curve to E
    context.line_to(x+r,y+h)                    # Line to F
    context.curve_to(x,y+h,x,y+h,x,y+h-r)       # Curve to G
    context.line_to(x,y+r)                      # Line to H
    context.curve_to(x,y,x,y,x+r,y)             # Curve to A
    return

def create_surface(w, h):
    data = (ctypes.c_ubyte * w * h * 4)()
    stride = w * 4
    surface = cairo.ImageSurface.create_for_data (data, cairo.FORMAT_ARGB32,
                                              w, h, stride);

    texture = pyglet.image.Texture.create_for_size(GL_TEXTURE_2D, w, h, GL_RGBA)

    return data, texture, surface


class ControlRenderer(object):

    def __call__(self, control, style, context):
        w = control.style['width']
        h = control.style['height']

        # TODO "something better"
        # quick hack to let dirty work...
        if control.dirty:
            try:
                del context['surface']
                del context['surface_data']
            except KeyError:
                pass
        try:
            surface = context['surface']
            data = context['surface_data'] 
            texture = context['texture']
        except KeyError:
            data, texture, surface = create_surface(w, h)
            context['surface'] = surface
            context['surface_data'] = data
            context['texture'] = texture
        
        glEnable(GL_TEXTURE_2D)
        glBindTexture(GL_TEXTURE_2D, texture.id)
        if control.dirty:
            ctx = cairo.Context(surface)
            self.redraw(control, style, context, w, h, ctx)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE,
                            data)
            control.dirty=False

        glBegin(GL_QUADS)
        glTexCoord2f (0.0, 1.0)
        glVertex2i(0, 0)
        glTexCoord2f(1.0, 1.0)
        glVertex2i(w, 0)
        glTexCoord2f(1.0, 0.0)
        glVertex2i(w, h)
        glTexCoord2f(0.0, 0.0)
        glVertex2i(0, h)
        glEnd()

    def redraw(self, control, style, context, w, h, ctx):
        self.draw_background(control, style, context, w, h, ctx)
        self.draw_border(control, style, context, w, h, ctx)

    def draw_background(self, control, style, context, w, h, ctx):
        if style['background_color']:
            ctx.set_source_rgb(*style['background_color'])
            if style['border_radius']:
                ctx.new_path()
                roundedrec(ctx, 2, 2, w-4, h-4)
                ctx.close_path()
                ctx.fill()
            else:
                ctx.paint()   
        else:
            ctx.set_source_rgba(0, 0, 0, 0)
            ctx.paint()

    def draw_border(self, control, style, context, w, h, ctx):
        if style['border_style'] and style['border_width'] > 0 and style['border_color']:
            ctx.set_line_width(style['border_width'])
            ctx.set_source_rgb(*style['border_color'])
            ctx.new_path()
            if style['border_radius']:
                roundedrec(ctx, 2, 2, w-4, h-4, style['border_radius'])
            else:
                pass
            ctx.stroke()
                

def draw_text(style, ctx, w, h, text, prefix=''):
    if (style[prefix+'font_color'] and 
        style[prefix+'font_face'] and 
        style[prefix+'font_size']):
        ctx.set_source_rgb(*style[prefix+'font_color'])
        ctx.select_font_face(style[prefix+'font_face'], False, False)
        ctx.set_font_size(style[prefix+'font_size'])
        extents = ctx.text_extents(text)
        #return Py_BuildValue("(dddddd)", extents.x_bearing, extents.y_bearing,
        #     extents.width, extents.height, extents.x_advance,
        #     extents.y_advance);

        x = (w/2)-(extents[2]/2 + extents[0])
        y = (h/2)-(extents[3]/2 + extents[1])

        ctx.move_to(x, y)
        ctx.show_text(text)


class LabelRenderer(ControlRenderer):
    def redraw(self, control, style, context, w, h, ctx):
        ControlRenderer.redraw(self, control, style, context, w, h, ctx)

        draw_text(style, ctx, w, h, control.label)


class FrameRenderer(ControlRenderer):
    def redraw(self, control, style, context, w, h, ctx):
        ControlRenderer.draw_background(self, control, style, context, w, h, ctx)
        self.draw_title(control, style, context, w, h, ctx)
        ControlRenderer.draw_border(self, control.label, style, context, w, h, ctx)

    def draw_title(self, control, style, context, w, h, ctx):
        draw_text(style, ctx, w, style['title_height'], control.label, prefix="title_")

        
class WindowRenderer(FrameRenderer):
    def draw_title(self, control, style, context, w, h, ctx):
        ctx.set_source_rgb(*style['title_background_color'])
        ctx.rectangle(2, 2, w-4, style['title_height']-4)
        ctx.fill()
        FrameRenderer.draw_title(self, control, style, context, w, h, ctx)
        

class CairoRenderer(windoh.Renderer):
    def __init__(self, style, initial=None):
        cairo_renderers = {
            'Control': ControlRenderer(),
            'Label': LabelRenderer(),
            'Frame': FrameRenderer(),
            'Window': WindowRenderer(),
        }
        if initial:
            cairo_renderers.update(initial)
        windoh.Renderer.__init__(self, style, cairo_renderers)


